package com.abso.sunlight.api;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.dom4j.Element;

/**
 * Legislator field's tag.
 */
@Retention(RetentionPolicy.RUNTIME)
@interface LegislatorField {

    /**
     * Returns the XML field name.
     * 
     * @return the XML field name.
     */
    public String value();
}

/**
 * Legislator.
 */
public class Legislator {

    /** The title held by this legislator. */
    @LegislatorField("title")
    private Title title;

    /** The legislator's first name. */
    @LegislatorField("firstname")
    private String firstName;

    /** The legislator's middle name or initial. */
    @LegislatorField("middlename")
    private String middleName;

    /** The legislator's last name. */
    @LegislatorField("lastname")
    private String lastName;

    /** The legislator's suffix (Jr., III, etc.). */
    @LegislatorField("name_suffix")
    private String nameSuffix;

    /** The preferred nickname of legislator (if any). */
    @LegislatorField("nickname")
    private String nickname;

    /** The legislator's political party. */
    @LegislatorField("party")
    private Party party;

    /** The legislator's state. */
    @LegislatorField("state")
    private State state;

    /** If legislator is a representative, their district. 0 is used for At-Large districts. */
    @LegislatorField("district")
    private String district;

    /** If legislator is currently serving or if legislator is no longer in office due to defeat/resignation/death/etc.. */
    @LegislatorField("in_office")
    private boolean inOffice;

    /** The legislator's gender. */
    @LegislatorField("gender")
    private Gender gender;

    /** The congressional office phone number. */
    @LegislatorField("phone")
    private String phone;

    /** The congressional office fax number. */
    @LegislatorField("fax")
    private String fax;

    /** The URL of Congressional website. */
    @LegislatorField("website")
    private String webSite;

    /** The URL of web contact form. */
    @LegislatorField("webform")
    private String webContactForm;

    /** The legislator's email address (if known). */
    @LegislatorField("email")
    private String email;

    /** The legislator's Washington DC Office Address. */
    @LegislatorField("congress_office")
    private String congressOffice;

    /** The legislator ID assigned by Congressional Biographical Directory (also used by Washington Post/NY Times). */
    @LegislatorField("bioguide_id")
    private String bioguideId;

    /** The legislator ID assigned by Project Vote Smart. */
    @LegislatorField("votesmart_id")
    private String voteSmartId;

    /** The Federal Election Commission ID. */
    @LegislatorField("fec_id")
    private String fecId;

    /** The ID assigned by GovTrack.us. */
    @LegislatorField("govtrack_id")
    private String govTrackId;

    /** The ID provided by Center for Responsive Politics. */
    @LegislatorField("crp_id")
    private String crpId;

    /** The performer ID on eventful.com. */
    @LegislatorField("eventful_id")
    private String eventfulId;

    /** The ID used by deprecated Sunlight Labs API (should not be used except for legacy purposes). */
    @LegislatorField("sunlight_old_id")
    private String sunlightOldId;

    /** The URL of Legislator's entry on Congresspedia. */
    @LegislatorField("congresspedia_url")
    private String congresspediaURL;

    /** The Congressperson's official Twitter account. */
    @LegislatorField("twitter_id")
    private String twitterId;

    /** The Congressperson's official Youtube account. */
    @LegislatorField("youtube_url")
    private String youTubeURL;

    /**
     * Constructs a new legislator.
     * 
     * @param elem
     *            the XML holding legislator information.
     * @throws SunlightException
     *             if an exception occurred creating the legislator.
     */
    public Legislator(Element elem) throws SunlightException {
        for (Field field : Legislator.class.getDeclaredFields()) {
            LegislatorField legislatorField = field.getAnnotation(LegislatorField.class);
            if (legislatorField != null) {
                String path = legislatorField.value();
                Object value = null;
                if (field.getType() == String.class) {
                    value = elem.valueOf(path);
                } else if (field.getType() == Title.class) {
                    value = Title.valueOfAbbreviation(elem.valueOf(path));
                } else if (field.getType() == Gender.class) {
                    value = Gender.valueOfAbbreviation(elem.valueOf(path));
                } else if (field.getType() == State.class) {
                    value = State.valueOfAbbreviation(elem.valueOf(path));
                } else if (field.getType() == Party.class) {
                    value = Party.valueOfAbbreviation(elem.valueOf(path));
                } else if (field.getType() == Boolean.TYPE) {
                    value = SunlightDataUtils.getBoolean(elem, path);
                } else {
                    throw new SunlightException("Unrecognized field type: " + field.getType());
                }
                if (value != null) {
                    try {
                        field.set(this, value);
                    } catch (IllegalArgumentException e) {
                        throw new SunlightException(e);
                    } catch (IllegalAccessException e) {
                        throw new SunlightException(e);
                    }
                }
            }
        }
    }

    /**
     * Returns the title held by this legislator.
     * 
     * @return the title held by this legislator.
     */
    public Title getTitle() {
        return title;
    }

    /**
     * Returns the legislator's first name.
     * 
     * @return the legislator's first name.
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Returns the legislator's middle name or initial.
     * 
     * @return the legislator's middle name or initial.
     */
    public String getMiddleName() {
        return middleName;
    }

    /**
     * Returns the legislator's last name.
     * 
     * @return the legislator's last name.
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Returns the legislator's suffix (Jr., III, etc.).
     * 
     * @return the legislator's suffix (Jr., III, etc.).
     */
    public String getNameSuffix() {
        return nameSuffix;
    }

    /**
     * Returns the preferred nickname of legislator (if any).
     * 
     * @return the preferred nickname of legislator (if any).
     */
    public String getNickname() {
        return nickname;
    }

    /**
     * Returns the legislator's political party.
     * 
     * @return the legislator's political party.
     */
    public Party getParty() {
        return party;
    }

    /**
     * Returns the legislator's state.
     * 
     * @return the legislator's state.
     */
    public State getState() {
        return state;
    }

    /**
     * Returns if legislator is a representative, their district. 0 is used for At-Large districts.
     * 
     * @return if legislator is a representative, their district. 0 is used for At-Large districts.
     */
    public String getDistrict() {
        return district;
    }

    /**
     * Returns if legislator is currently serving or if legislator is no longer in office due to defeat/resignation/death/etc..
     * 
     * @return if legislator is currently serving or if legislator is no longer in office due to defeat/resignation/death/etc..
     */
    public boolean isInOffice() {
        return inOffice;
    }

    /**
     * Returns the legislator's gender.
     * 
     * @return the legislator's gender.
     */
    public Gender getGender() {
        return gender;
    }

    /**
     * Returns the congressional office phone number.
     * 
     * @return the congressional office phone number.
     */
    public String getPhone() {
        return phone;
    }

    /**
     * Returns the congressional office fax number.
     * 
     * @return the congressional office fax number.
     */
    public String getFax() {
        return fax;
    }

    /**
     * Returns the URL of Congressional website.
     * 
     * @return the URL of Congressional website.
     */
    public String getWebSite() {
        return webSite;
    }

    /**
     * Returns the URL of web contact form.
     * 
     * @return the URL of web contact form.
     */
    public String getWebContactForm() {
        return webContactForm;
    }

    /**
     * Returns the legislator's email address (if known).
     * 
     * @return the legislator's email address (if known).
     */
    public String getEmail() {
        return email;
    }

    /**
     * Returns the legislator's Washington DC Office Address.
     * 
     * @return the legislator's Washington DC Office Address.
     */
    public String getCongressOffice() {
        return congressOffice;
    }

    /**
     * Returns the legislator ID assigned by Congressional Biographical Directory (also used by Washington Post/NY Times).
     * 
     * @return the legislator ID assigned by Congressional Biographical Directory (also used by Washington Post/NY Times).
     */
    public String getBioguideId() {
        return bioguideId;
    }

    /**
     * Returns the legislator ID assigned by Project Vote Smart.
     * 
     * @return the legislator ID assigned by Project Vote Smart.
     */
    public String getVoteSmartId() {
        return voteSmartId;
    }

    /**
     * Returns the Federal Election Commission ID.
     * 
     * @return the Federal Election Commission ID.
     */
    public String getFecId() {
        return fecId;
    }

    /**
     * Returns the ID assigned by GovTrack.us.
     * 
     * @return the ID assigned by GovTrack.us.
     */
    public String getGovTrackId() {
        return govTrackId;
    }

    /**
     * Returns the ID provided by Center for Responsive Politics.
     * 
     * @return the ID provided by Center for Responsive Politics.
     */
    public String getCrpId() {
        return crpId;
    }

    /**
     * Returns the performer ID on eventful.com.
     * 
     * @return the performer ID on eventful.com.
     */
    public String getEventfulId() {
        return eventfulId;
    }

    /**
     * Returns the ID used by deprecated Sunlight Labs API (should not be used except for legacy purposes).
     * 
     * @return the ID used by deprecated Sunlight Labs API (should not be used except for legacy purposes).
     */
    public String getSunlightOldId() {
        return sunlightOldId;
    }

    /**
     * Returns the URL of Legislator's entry on Congresspedia.
     * 
     * @return the URL of Legislator's entry on Congresspedia.
     */
    public String getCongresspediaURL() {
        return congresspediaURL;
    }

    /**
     * Returns the Congressperson's official Twitter account.
     * 
     * @return the Congressperson's official Twitter account.
     */
    public String getTwitterId() {
        return twitterId;
    }

    /**
     * Returns the Congressperson's official Youtube account.
     * 
     * @return the Congressperson's official Youtube account.
     */
    public String getYouTubeURL() {
        return youTubeURL;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
    }

    @Override
    public int hashCode() {
        return getBioguideId().hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Legislator)) {
            return false;
        }
        return getBioguideId().equals(((Legislator) obj).getBioguideId());
    }
}
